/*
 *
 * 
 * Rotary function
 * Using C in XC8. Adapted from http://www.eevblog.com/forum/projects/rotary-encoders-new-polling-and-pin-interrupt-servicing-routines/msg1416552/
 * 
 * It's highly recommended you read through the ASM code to understand
 * how this works if you don't already. You may want to rewrite it
 * in ASM to get a solid grasp.
 * 
 * Built with Pic18F4550
 * 
 * 
 */


#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#include "main.h"


// Each Rotary must have these two variables.
uint8_t prev1           = 0;
uint8_t targ1           = 0;

// These can be used for all Rotaries
uint8_t ENCODERTYPE     = 0; //rotary held high, pulled low on turn
uint8_t RESULT          = 0; 

void InitInterrupts(void){
    
    //Interrupts register. See data sheet.
    //7 global enable. 4 timer0 overflow enable
    INTCON = 0b10010000;
    //7 can also be implemented as below:
    GIE = 1; //global interrupt enable
    
    TMR0ON = 1; // timer on
    T0CS   = 0; // clock select
    TMR0IE = 1; // timer zero interrupt enable
    RBIE   = 0; // disable interrupts on PORTB data sheet for micro 9.9
    
}




/*
 * This rotary function
 * is best understood by 
 * implementing in asm from the eevblog post on your own
 * then converting to c 
 * for writing speed (I tried doing an asm
 * module, but had some trouble implementing, and
 * there was minimal speed gains if any, from the code 
 * I created.)
 */
uint8_t rotary(uint8_t port, uint8_t cwpin, uint8_t ccwpin, uint8_t pins, uint8_t *previous, uint8_t *target){
    
    uint8_t current;
    uint8_t xortest;
    uint8_t tick;
    
    //move PORT into current
    current = port;
    //only keep pins you want
    current = current & pins;
    if (current == *target){ //either true click or back to normal
        if (current == ENCODERTYPE){ //true click 
            tick = current;
            xortest = current ^ *previous; //See which pin clicked first last time (direction)
            if (xortest == cwpin){
                RESULT =  1;
                }
            if (xortest == ccwpin){
                RESULT = 0xFF;
                }
           // else {
           //     RESULT = -1; //invalid data
           //     }
        }
        //invert target
        *target = pins ^ *target;
    }
    //store value of current in previous (for determining left or right turn)
    *previous = current;
    return;
}
void rotarytest(void){
        
    
    // rotary in strict asm I had at 2-4 us each
    // from LATB3 high to low, depending on what
    // was processed (whether a click or skip. 2us for skip)
    // in my c it was roughly the same.
    // How fast can rotary decoding be?
    //LATBbits.LATB3 = 1;   //debug
    rotary(PORTD,0b00000001,0b00000010,0b00000011, &prev1, &targ1);
    //LATBbits.LATB3 = 0;   //debug
    
    ///attach led to LATC7
    if (RESULT == 1){
        LATCbits.LATC7 = 1;
        __delay_ms(100);
        LATCbits.LATC7 = 0;
        RESULT = 0;
        return;
    }
    //attach led to LATB3 
    if (RESULT == 0xFF){
        LATBbits.LATB3 = 1;
        __delay_ms(100);
        LATBbits.LATB3 = 0;
        RESULT = 0;
        return;
    }
    
 }


void interrupt Timer0_ISR(void){
    asm("NOP"); //breakpoint here for debugging
    
    //First
    //all rotary reads
    rotarytest();
    
    //Now you may enable interrupts again
    if (TMR0IE && TMR0IF){ //if timer int. enabled, and (IF == int. overflow) overflowed...)
        TMR0IF = 0; //software must reset timer
    }
}



void main(void) {
    
    //Things required to use digital input on ports
    
    //PORTB output B3 needs CCP disabled
    CCP1CON=0b00000000;
    CCP2CON=0b00000000;
    //disable spp on portb / d
    SPPEN=0;
    // enable all pins as digital inputs not analog 
    // ADC must configure ports if using digital input. 
    // Even if not using ADC.
    // AND, only certain pins can be used...
    // NOTE: required to use PORTB/D any port (disabling ADC basically)
    ADCON1 = 0b00001111;
    // 1 for pull ups off. 0 for pull ups on. For details see table 10-4 data sheet.
    RBPU   = 1;   //also defined in initinterrupts
    //rotary 1 inputs to PORTD0/1
    TRISDbits.TRISD0 = 1; //input
    TRISDbits.TRISD1 = 1; //input
    //debug
    TRISBbits.TRISB3 = 0; //output
    TRISCbits.TRISC7 = 0; //output
   
    
    
    
    //enable timer0
    InitInterrupts();
    
    
    
    while(1){
        
        asm("NOP");
    }
    
}
